package com.apobates.jforum2.troll.threads.biz.impl.dao;

import com.apobates.jforum2.troll.threads.biz.dao.TopicTagDao;
import com.apobates.jforum2.troll.threads.entity.TopicTag;
import com.apobates.jforum2.troll.utils.core.Commons;
import com.apobates.jforum2.troll.utils.persistence.Page;
import com.apobates.jforum2.troll.utils.persistence.Pageable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author xiaofanku
 * @since 20200513
 */
@Repository
public class TopicTagDaoImpl implements TopicTagDao{
    @PersistenceContext
    private EntityManager entityManager;
    @Value("${spring.jpa.batch.size}")
    private int batchSize;
    private final static Logger logger = LoggerFactory.getLogger(TopicTagDaoImpl.class);

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Optional<Boolean> remove(long id) {
        int affect = entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.id = ?1").setParameter(1, id).executeUpdate();
        return affect == 1 ? Optional.of(true) : Optional.empty();
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public Optional<Boolean> remove(long id, long topicId) {
        int affect = entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.id = ?1 AND tt.topicId = ?2").setParameter(1, id).setParameter(2, topicId).executeUpdate();
        return affect == 1 ? Optional.of(true) : Optional.empty();
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int removeAll(long topicId) {
        return entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.topicId = ?1").setParameter(1, topicId).executeUpdate();
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int batchSave(long topicId, Collection<TopicTag> tages) {
        //删除掉原来的标签
        entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.topicId = ?1").setParameter(1, topicId).executeUpdate();
        //logger.debug("[batchSave][TopicTagDao] delete exists tag size:" + affect);
        int i = 0;
        for (TopicTag tt : tages) {
            entityManager.persist(tt);
            i++;
            if (i % batchSize == 0) {
                // Flush a batch of inserts and release memory.
                entityManager.flush();
                entityManager.clear();
            }
        }
        return i;
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int batchSave(long topicId, Map<String, Integer> tages) {
        if (tages == null || tages.isEmpty()) {
            return 0;//("参数不合法或不被接受");
        }
        Set<TopicTag> rs = tages.entrySet().stream().map(entry -> new TopicTag(entry.getKey(), entry.getValue(), topicId)).collect(Collectors.toSet());
        return batchSave(topicId, rs);
    }
    
    @Override
    public List<TopicTag> findAllByTopic(long topicId) {
        return entityManager.createQuery("SELECT tt FROM TopicTag AS tt WHERE tt.topicId = ?1 AND tt.status = ?2", TopicTag.class).setParameter(1, topicId).setParameter(2, true).getResultList();
    }
    
    @Override
    public Stream<TopicTag> findAllRelateTopic(long topicId, int size) {
        return entityManager.createQuery("SELECT tt FROM TopicTag tt WHERE tt.status = ?1 AND EXISTS(SELECT 1 FROM TopicTag t1 WHERE t1.topicId = ?2 AND t1.status = ?3 AND t1.names = tt.names)", TopicTag.class)
                .setParameter(1, true)
                .setParameter(2, topicId)
                .setParameter(3, true)
                .setMaxResults(size)
                .getResultStream();
    }
    
    @Override
    public Stream<TopicTag> findAllRelateTopic(long topicId, Collection<String> tages) {
        if(null == tages || tages.isEmpty()){
            return Stream.empty();
        }
        //多少结果集
        int maxSize = tages.size() * 10;
        String param = Commons.quoteSQLParameters(tages).orElse(null);
        if (null == param) {
            return Stream.empty();
        }
        return entityManager.createQuery("SELECT tt FROM TopicTag tt WHERE tt.topicId != ?1 AND tt.status = ?2 AND tt.names IN(" + param + ")", TopicTag.class)
                .setParameter(1, topicId)
                .setParameter(2, true)
                .setMaxResults(maxSize)
                .getResultStream();
    }
    
    @Override
    public Map<String, Long> groupTagForNames(int size) {
        final String SQL = "SELECT tt.names, COUNT(tt) FROM TopicTag tt GROUP BY tt.names ORDER BY COUNT(tt) DESC";
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery(SQL).setMaxResults(size).getResultList();
        //
        Map<String, Long> data = new HashMap<>();
        for (Object[] columns : result) {
            String names = null;
            try {
                names = (String) columns[0];
            } catch (java.lang.ClassCastException e) {
            }
            Long v = 0L;
            try {
                v = ((BigDecimal) columns[1]).longValue();
            } catch (java.lang.ClassCastException e) {
                v = (Long) columns[1];
            }
            if (names == null) {
                continue;
            }
            if (v == null) {
                continue;
            }
            data.put(names, v);
        }
        return data;
    }
    
    @Override
    public Page<TopicTag> findAll(Pageable pageable) {
        final long total = count();
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicTag> query = entityManager.createQuery("SELECT tt FROM TopicTag tt ORDER BY tt.id DESC", TopicTag.class);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicTag> result = query.getResultStream();
        return new Page<TopicTag>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicTag> getResult() {
                return result;
            }
        };
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void save(TopicTag entity) {
        entityManager.persist(entity);
    }
    
    @Override
    public Optional<TopicTag> findOne(Long primaryKey) {
        return Optional.ofNullable(entityManager.find(TopicTag.class, primaryKey));
    }
    
    @Override
    public Optional<Boolean> edit(TopicTag updateEntity) {
        return Optional.empty();
    }
    
    @Override
    public Stream<TopicTag> findAll() {
        return Stream.empty();
    }
    
    @Override
    public long count() {
        try {
            return entityManager.createQuery("SELECT COUNT(tt) FROM TopicTag tt", Long.class).getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[count][TopicTagDao]", e);
            }
        }
        return 0L;
    }
}